using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Office.Interop.Outlook;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using TXLooker.Common;
using TXLooker.Common.Core;
using TXLooker.Properties;

namespace TXLooker.Core
{
    internal class OlServerProviderWrapper : OlProviderWrapperBase, IServerProvider
    {
        #region Class Properties

        protected ServerProvider ServerProvider
        {
            get { return this.m_provider; }
            private set
            {
                if( value == null ) throw new ArgumentException();
                this.m_provider = value;

                this.m_provider.NewProjects += new TFEventHandler<IEnumerable<ProjectProvider>>( OnNewProjects );
                this.m_provider.PreRemoveProjects += new TFEventHandler<IEnumerable<ProjectProvider>>( OnDeleteProjects );
            }
        }
        private ServerProvider m_provider = null;

        #endregion Class Properties

        #region class Constructors

        private OlServerProviderWrapper() : base(null)
        { }
        public OlServerProviderWrapper( ServerProvider provider ) : this()
        {
            this.ServerProvider = provider;
        }

        #endregion class Constructors

        #region IServerProvider Members

        public string ServerName
        {
            get { return this.ServerProvider.ServerName; }
        }
        public Uri ServerUri
        {
            get { return this.ServerProvider.ServerUri; }
        }
        public TeamFoundationServer Server
        {
            get { return this.ServerProvider.Server; }
        }
        public ICommonStructureService CommonServerStructure
        {
            get { return this.ServerProvider.CommonServerStructure; }
        }

        #region Projects

        public IEnumerable<IProjectProvider> Projects
        {
            get 
            {
               lock( this.m_projectMAP )
               {
                   foreach( IProjectProvider item in this.m_projectMAP.Values )
                       yield return item;
               }
            }
        }
        private Dictionary<Uri, OlProjectProviderWrapper> m_projectMAP = new Dictionary<Uri, OlProjectProviderWrapper>();

        #endregion Projects

        #region event NewProjects

        public event TFEventHandler<IEnumerable<IProjectProvider>> NewProjects
        {
            add    { lock( this.m_syncNewProjects ) { this.m_OnNewProjectsEH += value; } }
            remove { lock( this.m_syncNewProjects ) { this.m_OnNewProjectsEH -= value; } }
        }
        private TFEventHandler<IEnumerable<IProjectProvider>> m_OnNewProjectsEH = null;

        private readonly object m_syncNewProjects = new object();

        private void RaiseNewProjects( IEnumerable<IProjectProvider> projects )
        {
            CommonGlobal.RaiseEventHandler( this.m_OnNewProjectsEH, new object[] { this, projects }, m_syncNewProjects );
        }

        #endregion event NewProjects

        #region event PreRemoveProjects

        public event TFEventHandler<IEnumerable<IProjectProvider>> PreRemoveProjects
        {
            add { lock( this.m_syncPreRemoveProjects ) { this.m_OnPreRemoveProjectsEH += value; } }
            remove { lock( this.m_syncPreRemoveProjects ) { this.m_OnPreRemoveProjectsEH -= value; } }
        }
        private TFEventHandler<IEnumerable<IProjectProvider>> m_OnPreRemoveProjectsEH = null;

        private readonly object m_syncPreRemoveProjects = new object();

        private void RaisePreRemoveProjects( IEnumerable<IProjectProvider> projects )
        {
            CommonGlobal.RaiseEventHandler( this.m_OnPreRemoveProjectsEH, new object[] { this, projects }, m_syncPreRemoveProjects );
        }

        #endregion event PreRemoveProjects

        protected override void UpdateData()
        {
            this.ServerProvider.Update();
        }

        #endregion

        #region Handlers

        private void OnNewProjects( object sender, IEnumerable<ProjectProvider> projects )
        {
            List<IProjectProvider> olItems = new List<IProjectProvider>();

            //Create objects
            lock( this.m_projectMAP )
            {
                foreach( ProjectProvider item in projects )
                {
                    OlProjectProviderWrapper olProject = new OlProjectProviderWrapper( this, item );

                    //register project
                    this.m_projectMAP.Add( item.ProjectUri, olProject );

                    olItems.Add( olProject );
                }
            }

            //notify all clients
            this.RaiseNewProjects( olItems );
        }
        private void OnDeleteProjects( object sender, IEnumerable<ProjectProvider> projects )
        {
            List<IProjectProvider> olItems = new List<IProjectProvider>();

            lock( this.m_projectMAP )
            {
                foreach( ProjectProvider item in projects )
                {
                    olItems.Add( this.m_projectMAP[item.ProjectUri] );
                    this.m_projectMAP.Remove( item.ProjectUri );
                }
            }

            //sync exec
            this.RaisePreRemoveProjects( olItems );

            //delete folders from outlook
            foreach( OlProjectProviderWrapper item in olItems )
                item.Delete();
        }

        #endregion Handlers

        public override void InitFolder( MAPIFolder parent )
        {
            string serverFolderName = string.Format("Server: {0}", this.ServerProvider.ServerUri);

            MAPIFolder folder = GetFolderByName(parent, serverFolderName);
            if (folder == null)
                folder = parent.Folders.Add(serverFolderName, Missing.Value);

            lock (this.SyncFolderObject)
            {
                this.Folder = folder;
            }
        }

        private MAPIFolder GetFolderByName(MAPIFolder parent, string folderName)
        {
            foreach (MAPIFolder mapiFolder in parent.Folders)
            {
                if(string.CompareOrdinal(mapiFolder.Name, folderName) == 0)
                {
                    return mapiFolder;
                }
            }

            return null;
        }

    }
}
